/*
 *  Copyright 2013 by Sebastian Gesemann
 *
 *  This file is part of a SipHash++, a C++ SipHash implementation
 *
 *  SipHash is a family of pseudorandom functions (a.k.a. keyed hash
 *  functions) optimized for speed on short messages. It was designed
 *  by Jean-Philippe Aumasson and Daniel J. Bernstein. Check out
 *  https://131002.net/siphash/
 *
 *  SipHash++ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  SipHash++ is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with SipHash++.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include "siphash.hpp"

namespace {

template<int Words>
std::ostream& operator<<(std::ostream& os, siphash::array<Words> const& a)
{
	char temp[18] = " ";
	for (int i=0; i<Words; ++i) {
		auto v = a[i];
		for (int k=0; k<16; ++k)
			temp[1+k] = "0123456789ABCDEF"[(v >> (15-k)*4) & 0xF];
		os << (temp + (i==0));
	}
	return os;
}

template<int Words>
bool operator==(siphash::array<Words> const& a, siphash::array<Words> const& b)
{
	for (int i=0; i<Words; ++i)
		if (a[i]!=b[i]) return false;
	return true;
}


const unsigned char data[] = {
	0x00, 0x01, 0x02, 0x03, 0x04,
	0x05, 0x06, 0x07, 0x08, 0x09,
	0x0A, 0x0B, 0x0C, 0x0D, 0x0E,  0x0F
};

const siphash::state_t v_expected = {{
	0xf6bcd53893fecff1ull, 0x54b9964c7ea0d937ull,
	0x1b38329c099bb55aull, 0x1814bb89ad7be679ull
}};

const siphash::uint64_t h_expected = 0xa129ca6149be45e5ull;


inline bool verbose(bool ok)
{
	std::cout << "OK\n\0FAILED\n"+(!ok*4);
	return ok;
}

bool test1()
{
	std::cout << "test1, init/upda/finish : ";
	auto k = siphash::key(data);
	auto v = init(k);
	update2(v,k[0]);
	update2(v,k[1]);
	auto h = finish4(v);
	return verbose(h==h_expected && v==v_expected);
}

bool test2()
{
	std::cout << "test2, atonce           : ";
	auto h = siphash::atonce24(siphash::key(data),15,data);
	return verbose(h==h_expected);
}

bool test3()
{
	auto k = siphash::key(data);
	siphash::stream24_t sh;
	sh.reset(k); sh.update(15,data);                        auto h1 = sh.finish();
	sh.reset(k); sh.update( 5,data); sh.update(10,data+ 5); auto h2 = sh.finish();
	sh.reset(k); sh.update(10,data); sh.update( 5,data+10); auto h3 = sh.finish();
	sh.reset(k); sh.update( 8,data); sh.update( 7,data+ 8); auto h4 = sh.finish();
	sh.reset(k); sh.update( 4,data);
	          sh.update( 4,data+ 4); sh.update( 7,data+ 8); auto h5 = sh.finish();
	bool allok = true;
	std::cout << "test3-1, streaming  15  : "; allok &= verbose(h1==h_expected);
	std::cout << "test3-2, streaming  5/10: "; allok &= verbose(h2==h_expected);
	std::cout << "test3-3, streaming 10/5 : "; allok &= verbose(h3==h_expected);
	std::cout << "test3-4, streaming  8/7 : "; allok &= verbose(h4==h_expected);
	std::cout << "test3-5, streaming 4/4/7: "; allok &= verbose(h5==h_expected);
	return allok;
}

} // anonymous namespace

int main()
{
	bool allok = true;
	allok &= test1();
	allok &= test2();
	allok &= test3();
	return allok
		? EXIT_SUCCESS
		: EXIT_FAILURE;
}
